package com.sh.data.engine.web.aspect;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.sh.data.engine.common.annotation.permissionCheck.PermissionCheckMethod;
import com.sh.data.engine.common.constants.HttpStatus;
import com.sh.data.engine.common.constants.code.BizCodes;
import com.sh.data.engine.common.enumDefinition.PermissionCheckEnum;
import com.sh.data.engine.common.enumDefinition.RoleEnum;
import com.sh.data.engine.common.exception.BusinessException;
import com.sh.data.engine.common.util.ServletUtils;
import com.sh.data.engine.domain.authority.model.domain.RoleDomain;
import com.sh.data.engine.domain.authority.model.mapper.SimpleMenuEntityMapper;
import com.sh.data.engine.domain.authority.service.RoleMenuService;
import com.sh.data.engine.domain.authority.service.RoleService;
import com.sh.data.engine.domain.authority.service.UserProjectService;
import com.sh.data.engine.domain.base.model.UserContext;
import com.sh.data.engine.domain.workspace.manager.model.domain.UserDomain;
import com.sh.data.engine.repository.dao.authority.entity.RoleEntity;
import com.sh.data.engine.repository.dao.authority.entity.RoleMenuEntity;
import com.sh.data.engine.repository.dao.authority.entity.UserProjectEntity;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;


@Aspect
@Component
public class PermissionCheckAop {

    //该id在不同的环境不一样
    @Value("${super.admin.id}")
    private String SUPER_ADMIN;

    @Autowired
    private RoleService roleService;

    @Autowired
    private RoleMenuService roleMenuService;

    @Autowired
    private SimpleMenuEntityMapper simpleMenuEntityMapper;

    @Autowired
    private UserProjectService userProjectService;


    @Pointcut("@annotation(com.sh.data.engine.common.annotation.permissionCheck.PermissionCheckMethod)")
    public void checkPermission() {
    }

    @Around("checkPermission()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        // 获取方法上的注解，看是否有permissionCheck注解
        PermissionCheckMethod cpMethod = ((MethodSignature) pjp.getSignature()).getMethod().getAnnotation(PermissionCheckMethod.class);
        if (cpMethod != null) {
//            System.out.println("访问的方法上有@PermissionCheck注解，开始验证权限");

            if (ServletUtils.getProjectId() == null) {
                if (Objects.equals(UserContext.getUserId(), SUPER_ADMIN)) {
                    return pjp.proceed();
                } else {
                    throw new BusinessException(BizCodes.NO_ACCESS_PERMISSION, HttpStatus.FORBIDDEN);
                }
            }
            // 获取当前登录用户的用户id、角色id、工作空间
            UserDomain userInfo = UserContext.getUserInfo();
            int currentRoleId = userInfo.getRoleId();
            String currentUserId = userInfo.getId();
            Long projectId = ServletUtils.getProjectId();
            // 如果当前用户角色为超管，则无需验证权限
            if (Objects.equals(UserContext.getUserId(), SUPER_ADMIN)) {
                return pjp.proceed();
            }

            // 查询userProject表当前用户在当前空间的角色是否与获取的角色id一致，一致则有权限访问
            if (hasPermission(currentUserId, projectId, currentRoleId, cpMethod)) {
                return pjp.proceed();
            } else {
                throw new BusinessException(BizCodes.NO_ACCESS_PERMISSION, HttpStatus.FORBIDDEN);
            }
        } else {
            return pjp.proceed();
        }
    }

    private boolean hasPermission(String currentUserId, Long projectId,
                                  int currentRoleId, PermissionCheckMethod cpMethod) {
        LambdaQueryWrapper<UserProjectEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserProjectEntity::getUserId, currentUserId);
        queryWrapper.eq(UserProjectEntity::getProjectId, projectId);

        List<UserProjectEntity> userProjectEntities = userProjectService.list(queryWrapper);
        if (userProjectEntities.isEmpty()) {
            throw new BusinessException(BizCodes.NO_MATCHING_RECORD_ERROR, HttpStatus.BAD_REQUEST);
        }

        // 现逻辑：一个用户在当前空间中只有一个角色
        int roleId = userProjectEntities.get(0).getRoleId();
        if (roleId == currentRoleId) {
            RoleEnum[] allowedRoles = cpMethod.roles();
            if (allowedRoles.length == 0 || Arrays.stream(allowedRoles).anyMatch(role -> role.getStatus() == roleId)) {
                return true;
            } else {
                throw new BusinessException(BizCodes.NO_ACCESS_PERMISSION, HttpStatus.FORBIDDEN);
            }
        }
        // 有特定逻辑的权限验证
        PermissionCheckEnum strategy = cpMethod.value();
        return switch (strategy) {
            case NONE -> true;
            case EDIT_ROLE -> hasPermissionToEditRole(roleId);
        };
    }

    /**
     * 判断当前角色是否有角色管理权限
     * 当前角色若为系统角色，则1L和2L角色有权限编辑角色
     * 若当前角色为自定义角色，看是否模块权限里勾选了空间角色-编辑，是则有权限编辑新增删除角色
     */
    private boolean hasPermissionToEditRole(int roleId) {
        RoleEntity roleEntity = roleService.getById(roleId);

        if (roleEntity.getIsSystem() == null || roleEntity.getIsSystem() != 1) {
            // 找到该角色的拥有的菜单
            List<RoleMenuEntity> list =
                    roleMenuService.list(
                            new LambdaQueryWrapper<RoleMenuEntity>().eq(RoleMenuEntity::getRoleId, roleId));

            List<RoleDomain.SimpleMenuEntity> simpleMenuEntities = simpleMenuEntityMapper.map(list);
            if (simpleMenuEntities.stream().anyMatch(e -> e.getMenuId() == 50L && e.getReadWriteStatus() == 1)) {
                return true;
            }
            throw new BusinessException(BizCodes.NO_PERMISSION_TO_EDIT_ROLE, HttpStatus.FORBIDDEN);

        } else if (roleId == RoleEnum.PROJECT_OWNER.getStatus() || roleId == RoleEnum.PROJECT_ADMIN.getStatus()) {
            return true;
        }
        throw new BusinessException(BizCodes.NO_PERMISSION_TO_EDIT_ROLE, HttpStatus.FORBIDDEN);
    }
}


